package com.weixin.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.weixin.exception.WeiXinPropertyNullPointException;
import com.weixin.po.AccessToken;
import com.weixin.po.MyX509TrustManager;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;

/**
 * 处理微信接口的相关信息
 * 
 * @author asus
 */
public class WeiXinUtil {
	private static Logger logger = LogManager.getLogger();
	private static String OPENID = "openid";

	private static final String get_access_token_url = "https://api.weixin.qq.com/cgi-bin/token?"
			+ "grant_type=client_credential&appid=APPID&secret=APPSECRET";

	// 获取用户信息
	private static final String get_user_info_url = "https://api.weixin.qq.com/sns/userinfo?"
			+ "access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
	// 获取openid
	private static final String WEB_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?"
			+ "appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code";
	/* 获取access_token的接口地址（GET）限200（次/天） */
	public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?"
			+ "grant_type=client_credential&appid=APPID&secret=APPSECRET";
	// 通过code获取特殊的access_token用以获得用户信息
	public final static String get_special_access_by_code_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

	/**
	 * 根据code获取用户openid
	 * 
	 * @param code
	 * @return
	 * @throws IOException
	 */
	public static String getOpenidByCode(String code) throws IOException {

		if (code == null) {
			return null;
		}

		// 通过code换取网页授权access_token
		String url = WEB_ACCESS_TOKEN_URL.replace("CODE", code).replace("APPID", WeixinConfigUtil.getAppId())
				.replace("APPSECRET", WeixinConfigUtil.getAppSecret());
		System.out.println(url);

		JSONObject access_token_web = httpRequest(url, "GET", null);

		// System.out.println("errcode"+access_token_web.get("errcode"));
		// 如果用code获取用户opengid失败
		if (access_token_web.get("errcode") != null) {
			System.out.println("通过code获取用户id失败");
			return null;
		}

		// 通过access_token_web获取用户的openid
		String openid = (String) access_token_web.get(OPENID);
		// System.out.println(Context.OPENID + ":" + openid);
		System.out.println("获取openid成功");
		return openid;
	}

	/**
	 * 发起https请求并获取结果
	 * 
	 * @param requestUrl
	 *            请求地址
	 * @param requestMethod
	 *            请求方式（GET、POST）
	 * @param outputStr
	 *            提交的数据
	 * @return JSONObject （通过JSONObejct.get(key) 的方式获取json对象的属性值）
	 */
	public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {

		logger.debug("requestUrl    : " + requestUrl);
		logger.debug("requestMethod : " + requestMethod);
		logger.debug("outputStr     : " + outputStr);

		JSONObject jsonObject = null;
		StringBuffer buffer = new StringBuffer();
		try {
			// 创建SSLContext对象，并使用我们指定的信任管理器初始化
			TrustManager[] tm = { new MyX509TrustManager() };
			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
			sslContext.init(null, tm, new java.security.SecureRandom());
			// 从上述SSLContext对象中获得SSLSocketFactory对象
			SSLSocketFactory ssf = sslContext.getSocketFactory();

			URL url = new URL(requestUrl);
			HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssf);

			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			httpUrlConn.setRequestMethod(requestMethod);

			if ("GET".equalsIgnoreCase(requestMethod))
				httpUrlConn.connect();

			// 当有数据需要提交时
			if (null != outputStr) {
				OutputStream outputStream = httpUrlConn.getOutputStream();
				// 注意编码格式，防止中文乱码
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}

			// 将返回的输入流转换成字符串
			InputStream inputStream = httpUrlConn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

			String str = null;
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			bufferedReader.close();
			inputStreamReader.close();
			// 释放资源
			inputStream.close();
			inputStream = null;
			httpUrlConn.disconnect();
			jsonObject = JSONObject.fromObject(buffer.toString());
			// jsonObject = new JSONObject(buffer.toString());
			// 这里输出创建菜单的返回结果
			logger.warn("结果" + buffer.toString());

		} catch (ConnectException ce) {
			logger.error("Weixin server connection timed out.");
			// log.error("Weixin server connection timed out.");
		} catch (Exception e) {
			logger.debug("https request error:{}");
			logger.warn("catch exception : " + WeixinCommonUtil.getStackTrace(e));
			// log.error("https request error:{}", e);
		}
		return jsonObject;
	}

	/**
	 * 获取access_token ---它是有调用次数限制的，到时候看看有效期存放多久合适；
	 * 
	 * @param appid
	 *            凭证
	 * @param appsecret
	 *            密钥
	 * @return
	 * @throws SQLException
	 * @throws WeiXinPropertyNullPointException
	 * @throws IOException
	 */
	public static synchronized AccessToken getAccessToken()
			throws WeiXinPropertyNullPointException, SQLException, IOException {
		logger.debug("to get accesstoken");
		AccessToken accessToken = WeiXinDBFactory.getAccessToken();
		// AccessToken accessToken = null;

		// 若第一次获取accessToken
		// if (accessToken == null || !accessToken.validate()) {
		if (true) {
			logger.debug("to get AccessToken");
			String requestUrl = access_token_url.replace("APPID", WeixinConfigUtil.getAppId()).replace("APPSECRET",
					WeixinConfigUtil.getAppSecret());
			logger.debug("url : " + requestUrl);

			JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
			// 如果请求成功
			if (null != jsonObject && jsonObject.get("errcode") == null) {
				logger.debug("get AccessToken and result is not null");
				try {
					// before we should judge that if is token exist , but now
					// we store every token with different identification and
					// timestamp
					// boolean isfisrt_flag = false;
					if (accessToken == null) {
						// isfisrt_flag = true;
						accessToken = new AccessToken();
					}

					accessToken.setToken(jsonObject.getString("access_token"));
					accessToken.setTime(Calendar.getInstance().getTimeInMillis());
					// if (isfisrt_flag) {
					// 插入
					logger.info("插入新的accessToken");
					WeiXinDBFactory.insertAccessToken(accessToken);
					// } else {
					// 更新
					// logger.info("更新新的accessToken");
					// WeiXinDBFactory.saveAccessToken(accessToken);
					// }

				} catch (JSONException e) {
					accessToken = null;
					// 获取token失败
					// log.error("fail to get token errcode:{} errmsg:{}",
					// jsonObject.getInteger("errcode"),
					// jsonObject.getString("errmsg"));
					logger.debug("fail to get set accesstoken");
				}
			} else {
				// 获取accesstoken失败
				logger.debug("fail to get get accesstoken");
			}
		} else {
			logger.debug("accesstoken is valid");
		}

		return accessToken;
	}

	/**
	 * 获取用户信息
	 * 
	 * @return
	 */
	public static JSONObject getUserInfoByOpenid(String openid, String access_token) {

		if (openid == null) {
			logger.error("openid is null 无法通过openid获取用户信息");
			return null;
		}

		// AccessToken token = getAccessToken(configService.getAppid(),
		// configService.getAppsecret());

		if (access_token != null && !access_token.isEmpty()) {

			String url = get_user_info_url.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid);
			logger.debug("url:" + url);

			JSONObject user_info = httpRequest(url, "GET", null);

			// 用openid获取用户基本信息失败
			if (user_info.get("errcode") != null) {
				System.out.println("通过code获取用户id失败");
				return null;
			} else {
				return user_info;
			}
		}

		return null;
	}

	/**
	 * // * 通过code换取特殊的用以获取用户信息的access_token
	 * 
	 * @param code
	 * @return
	 * @throws IOException
	 */
	public static Map<String, String> getSpecialAccessTokenForUserInfoByCode(String code) throws IOException {

		Map<String, String> result = new HashMap<>();

		logger.debug("to get special access_token for userinfo");
		String requestUrl = get_special_access_by_code_url.replace("APPID", WeixinConfigUtil.getAppId())
				.replace("SECRET", WeixinConfigUtil.getAppSecret()).replace("CODE", code);
		logger.debug("the url :" + requestUrl);
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
		String access_token = "";
		if (jsonObject.get("errcode") == null) {
			// 消息获取正常
			access_token = jsonObject.getString("access_token");
			String openid = jsonObject.getString(OPENID);
			result.put("access_token", access_token);
			result.put("openid", openid);
		}
		return result;
	}

	public static String getJsApiTicket(String access_token)
			throws WeiXinPropertyNullPointException, SQLException, ParseException {
		logger.debug("accessToken : " + access_token);
		String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
		String requestUrl = url.replace("ACCESS_TOKEN", access_token.trim());
		// 发起GET请求获取凭证
		JSONObject jsonObject = httpRequest(requestUrl, "GET", null);

		String ticket = null;
		HashMap<String, Object> hm = new HashMap<String, Object>();

		hm = (HashMap<String, Object>) WeiXinDBFactory.getWeiXinJsApiTicket();
		// 若，第一次申请ticket
		if (hm == null || hm.size() == 0) {
			logger.debug("第一次申请ticket");
			if (null != jsonObject) {
				try {
					ticket = jsonObject.getString("ticket");
				} catch (JSONException e) {
				}
			}
			// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd
			// HH:mm:ss SSS");
			// 插入数据
			WeiXinDBFactory.insertWeiXinJsApiTicket(ticket, String.valueOf((Calendar.getInstance().getTimeInMillis())));
		} else {
			Calendar c = Calendar.getInstance();
			long timestamp = ((Timestamp) hm.get("timestamp")).getTime();
			// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd
			// HH:mm:ss SSS");
			// logger.debug("timestamp : " + timestamp);
			// Date date = dateFormat.parse(timestamp);

			long duration = Math.abs(c.getTimeInMillis() - timestamp);
			if (duration > 3600000) {
				logger.debug("旧的ticket已经过期，更新ticket , new ticket is " + ticket);
				if (null != jsonObject) {
					try {
						logger.debug("ticket : " + jsonObject.getString("ticket"));
						logger.debug("errcode : " + jsonObject.getString("errcode"));
						logger.debug("errmsg : " + jsonObject.getString("errmsg"));
						logger.debug("ticket : " + jsonObject.getString("ticket"));
						logger.debug("expires_in : " + jsonObject.getString("expires_in"));

						ticket = jsonObject.getString("ticket");
					} catch (JSONException e) {
					}
				}
				// 更新数据
				WeiXinDBFactory.updateWeiXinJsApiTicket(ticket,
						String.valueOf(Calendar.getInstance().getTimeInMillis()));
			} else {
				logger.debug("使用旧的的ticket");
				ticket = hm.get("ticket").toString();
			}
		}
		return ticket;
	}
}
